"""Scratch Plate Selector
.. helpdoc::
This is a somewhat specialized widget made for performing scratch plate assays from images obtained from a scratch plate.  The user will select areas that are scratched either left to right, or top to bottom along a grid.  The user can select the type of scratch that they are selecting.  When the user clicks an area of the grid it is filled with the colors that corespond to the type of scratch.  Areas are then used as the initial masks for calculating wounding distance.

"""

"""
<widgetXML>    
    <name>Scratch Plate Selector</name>
    <icon>default.png</icon>
    <tags> 
        <tag>Matplotlib Plotting</tag> 
    </tags>
    <summary>Apply an image transformation to an image.</summary>
    <author>
        <authorname>Kyle R. Covington</authorname>
        <authorcontact>kyle@red-r.org</authorcontact>
    </author>
</widgetXML>
"""
from OWRpy import * 
import redRGUI, signals 
import numpy, pylab
import scipy.ndimage as ndi
import matplotlib.path as mpath
import matplotlib.patches as mpatches
import redRi18n
_ = redRi18n.get_(package = 'base')
class scratchPlateAreaPicker(OWRpy):
    def __init__(self, **kwargs):
        OWRpy.__init__(self, **kwargs)
        self.data=None
        self.workingColor = ['#ff0000', '#00ff00', '#0000ff']
        self.mask = []
        self.plotSelectionState = 0
        self.currentSelect = None
        """.. rrsignals::"""
        self.inputs.addInput('id0', _('Image'), signals.base.NumpyArray, self.processImage)

        """.. rrsignals::"""
        self.outputs.addOutput('id0', _('Image'), signals.base.NumpyArray)

        
        self.GUI_plot = redRGUI.RedRMatplotlib.mplPlot(self.controlArea, callback = self.areaClicked)
        
        areaDefineBox = redRGUI.base.widgetBox(self.controlArea, orientation = 'horizontal')
        self.GUI_orientation = redRGUI.base.comboBox(areaDefineBox, label = "Assign Orientation:", items = [(1, "L to R"), (2, "T to B"), (0, "Clear")])
        self.GUI_width = redRGUI.base.spinBox(areaDefineBox, label = "Width Setting:", value = 500, max = 10000)
        self.GUI_height = redRGUI.base.spinBox(areaDefineBox, label = "Height Setting:", value = 500, max = 10000)
        self.GUI_useClamp = redRGUI.base.TFCheckBox(areaDefineBox, label = "Use Defined Width and Height", setChecked = True)
        
        horBox = redRGUI.base.widgetBox(self.controlArea, orientation = 'horizontal')
        
        self.GUI_clearAllButton = redRGUI.base.button(horBox, label = "Clear All", callback = self.clearAllSelections)
        self.GUI_commitItterator = redRGUI.base.button(horBox, label = "Commit Itterator", callback = self.commitItterator)
        self.GUI_commitItteratorOnConnect = redRGUI.base.TFCheckBox(horBox, label = "Commit Itterator On Check:", setChecked = True)
        self.GUI_sectionsPerMask = redRGUI.base.spinBox(horBox, label = "Sections Per Mask:", min = 1, decimal = 0, value = 5)
        redRGUI.base.button(self.bottomAreaRight, label = "Redraw", callback = self.redraw)
        self.commitButton = redRGUI.base.commitButton(self.bottomAreaRight, label = "Commit", callback = self.commitFunction, processOnInput = True)
    def redraw(self):
        if self.data == None: return
        self.GUI_plot.subPlot.clear()
        self.GUI_plot.canvas.draw()
        self.GUI_plot.subPlot.imshow(self.data)
        self.GUI_plot.subPlot.axis('off')
        self.GUI_plot.canvas.draw()
        self.setMask()
    def processImage(self, data):
        if data:
            self.data=data.getData()
            
            
            if self.GUI_commitItteratorOnConnect.checked() == "TRUE" and self.mask != []:
                self.commitItterator()
            else:
                self.redraw()
        else:
            self.data=None
    def clearAllSelections(self):
        """Clear all of the selected elements in the grid and assign the mask to []"""
        self.mask = []
        self.plotSelectionState = 0
        if self.data == None: return
        self.GUI_plot.subPlot.clear()
        self.GUI_plot.canvas.draw()
        self.GUI_plot.subPlot.imshow(self.data)
        self.GUI_plot.subPlot.axis('off')
        self.GUI_plot.canvas.draw()
    def areaClicked(self, event):
        if event.xdata == None or event.ydata == None: return
        if self.GUI_orientation.currentId() == 0:
            for i in range(len(self.mask)):
                if event.xdata > min([self.mask[i][1], self.mask[i][3]]) and event.xdata < max([self.mask[i][1], self.mask[i][3]]) and event.ydata > min([self.mask[i][2], self.mask[i][4]]) and event.ydata < max([self.mask[i][2], self.mask[i][4]]):
                    del self.mask[i]
                    
                    self.setMask()
                    return
        if (self.plotSelectionState == 0 or self.currentSelect == None) and self.GUI_useClamp.checked() == "FALSE":
            
            self.currentSelect = (event.xdata, event.ydata)
            self.plotSelectionState = 1
        elif self.GUI_useClamp.checked() == "TRUE":
            self.currentPath = mpath.Path
            if self.GUI_orientation.currentId() == 1:
                self.mask.append((self.GUI_orientation.currentId(), event.xdata, event.ydata, event.xdata + self.GUI_width.value(), event.ydata + self.GUI_height.value()))
            elif self.GUI_orientation.currentId() == 2:
                self.mask.append((self.GUI_orientation.currentId(), event.xdata, event.ydata, event.xdata + self.GUI_height.value(), event.ydata + self.GUI_width.value()))
            else: return
            self.plotMask(self.mask[-1])
        else:
            
            self.currentPath = mpath.Path
            
            self.mask.append((self.GUI_orientation.currentId(), self.currentSelect[0], self.currentSelect[1], event.xdata, event.ydata))
            self.plotMask(self.mask[-1])
            self.plotSelectionState = 0
            self.currentSelect = None
    def plotMask(self, mask):
        currentPathData = []
        currentPathData.append((self.currentPath.MOVETO, (mask[1], mask[2])))
        currentPathData.append((self.currentPath.LINETO, (mask[1], mask[4])))
        currentPathData.append((self.currentPath.LINETO, (mask[3], mask[4])))
        currentPathData.append((self.currentPath.LINETO, (mask[3], mask[2])))
        currentPathData.append((self.currentPath.CLOSEPOLY, (mask[3], mask[2])))
        codes, verts = zip(*currentPathData)
        path = mpath.Path(verts, codes)
        patch = mpatches.PathPatch(path, facecolor = self.workingColor[mask[0]], edgecolor = 'blue', alpha = 0.5)
                
        self.GUI_plot.subPlot.add_patch(patch)
        self.GUI_plot.canvas.draw()
    def setMask(self):
        if len(self.mask) == 0: return 
        for m in self.mask:
            self.plotMask(m)
            
    def getClippings(self, mask, clip):
        """The mask is the image section that has been selected for measurement, the clip is the number of clips that are to be made
        returns x1, y1, x2, y2 
        """
        if mask[0] == 1: # case L to R
            dx = int((max([mask[1], mask[3]])-min([mask[1], mask[3]]))/float(self.GUI_sectionsPerMask.value()))
            
            return (min([mask[1], mask[3]]) + (dx*clip), min([mask[2], mask[4]]), min([mask[1], mask[3]]) + (dx*(clip+1)), max([mask[2], mask[4]]))
        elif mask[0] == 2: # case T to B
            dy = int((max([mask[2], mask[4]])-min([mask[2], mask[4]]))/float(self.GUI_sectionsPerMask.value()))
            
            return (min([mask[1], mask[3]]), min([mask[2], mask[4]]) + (dy*clip), max([mask[1], mask[3]]), min([mask[2], mask[4]]) + (dy*(clip+1)))
    def commitItterator(self):
        if self.data == None: 
            self.status.setText("No image available")
            return
        for mask in self.mask:
            print mask
            for c in range(int(self.GUI_sectionsPerMask.value())):
                x1, y1, x2, y2 = self.getClippings(mask, c)
                print x1, y1, x2, y2
                newData = signals.base.NumpyArray(self, data = self.data[int(y1):int(y2), int(x1):int(x2)])
                self.rSend('id0', newData)
    def commitFunction(self):
        """Slice the image on the first mask at the first section"""
        if self.data == None: 
            self.status.setText("No image available")
            return
        mask = self.mask[0]
        print mask
        x1, y1, x2, y2 = self.getClippings(mask, 0)
        newArray = self.data[int(y1):int(y2), int(x1):int(x2)]
        newData = signals.base.NumpyArray(self, data = newArray)
        self.rSend('id0', newData)